﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a rational polynomial for real numbers.
    /// </summary>
    [Serializable]
    public class SimpleRationalPolynomial : AbstractRealPolynomial, ISimpleRationalPolynomial, IRealFunction
    {
        /// <summary>
        /// The numerator value of the rational polynomial.
        /// </summary>
        private SimplePolynomial numeratorPolynomial;

        /// <summary>
        /// The denumerator value of the rational polynomial.
        /// </summary>
        private SimplePolynomial denominatorPolynomial;

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleRationalPolynomial"/> class.
        /// </summary>
        /// <param name="numeratorPolynomial">The numerator value of the rational polynomial.</param>
        /// <param name="denominatorPolynomial">The denumerator value of the rational polynomial.</param>
        public SimpleRationalPolynomial(SimplePolynomial numeratorPolynomial, SimplePolynomial denominatorPolynomial)
        {
            if (numeratorPolynomial == (SimplePolynomial) null)
            {
                throw new ArgumentNullException("numeratorPolynomial");
            }

            if (denominatorPolynomial == (SimplePolynomial) null)
            {
                throw new ArgumentNullException("denominatorPolynomial");
            }

            this.numeratorPolynomial = numeratorPolynomial.Copy();
            this.denominatorPolynomial = denominatorPolynomial.Copy();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleRationalPolynomial"/> class.
        /// </summary>
        /// <param name="polynomial">The rational polynomial to clone.</param>
        public SimpleRationalPolynomial(SimpleRationalPolynomial polynomial)
        {
            if (polynomial == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("polynomial");
            }

            this.numeratorPolynomial = polynomial.NumeratorPolynomial.Copy();
            this.denominatorPolynomial = polynomial.DenominatorPolynomial.Copy();
        }

        /// <summary>
        /// Gets or sets the numerator value of the rational polynomial.
        /// </summary>
        /// <value>The numerator value of the rational polynomial.</value>
        public SimplePolynomial NumeratorPolynomial
        {
            get { return numeratorPolynomial; }
            set { numeratorPolynomial = value; }
        }

        /// <summary>
        /// Gets or sets the denumerator value of the rational polynomial.
        /// </summary>
        /// <value>The denumerator value of the rational polynomial.</value>
        public SimplePolynomial DenominatorPolynomial
        {
            get { return denominatorPolynomial; }
            set { denominatorPolynomial = value; }
        }

        /// <summary>
        /// Copies the current instance of a polynomial.
        /// </summary>
        /// <returns>The copied instance of the current polynomial.</returns>
        public SimpleRationalPolynomial Copy()
        {
            return new SimpleRationalPolynomial(this.numeratorPolynomial, this.denominatorPolynomial);
        }

        /// <summary>
        /// Compares the current instance of a polynomial to another one.
        /// </summary>
        /// <param name="compare">The polynomial to compare.</param>
        /// <returns>True if the polynomials are even otherwise, false.</returns>
        public bool CompareTo(SimpleRationalPolynomial compare)
        {
            return (this == compare);
        }

        /// <summary>
        /// Solves the polynomial at the specified position x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>
        /// The result f(x) at the specified position x.
        /// </returns>
        public override double SolveAt(double x)
        {
            double a = this.numeratorPolynomial.SolveAt(x);
            double b = this.numeratorPolynomial.SolveAt(x);

            if (b != 0)
            {
                return a / b;
            }

            throw new DomainNotDefinedException("Polynomial is not defined for x/0");
        }

        /// <summary>
        /// Creates the first derivative of the polynomial.
        /// </summary>
        /// <returns>Returns the derivative of the polynomial.</returns>
        public SimpleRationalPolynomial Derivative()
        {
            SimplePolynomial fd = this.numeratorPolynomial.Derivative();
            SimplePolynomial gd = this.denominatorPolynomial.Derivative();

            return new SimpleRationalPolynomial((fd * this.denominatorPolynomial)
                                                - (gd * this.numeratorPolynomial), this.denominatorPolynomial
                                                                                   * this.denominatorPolynomial);
        }

        /// <summary>
        /// Multiplies the polynomial with a scalar.
        /// </summary>
        /// <param name="scalar">The scalar value.</param>
        public void MultiplyWithScalar(int scalar)
        {
            this.numeratorPolynomial.MultiplyWithScalar(scalar);
            this.denominatorPolynomial.MultiplyWithScalar(scalar);
        }

        /// <summary>
        /// Multiplies the polynomial with a scalar.
        /// </summary>
        /// <param name="scalar">The scalar value.</param>
        public void MultiplyWithScalar(double scalar)
        {
            this.numeratorPolynomial.MultiplyWithScalar(scalar);
            this.denominatorPolynomial.MultiplyWithScalar(scalar);
        }

        /// <summary>
        /// Divides the polynomial through scalar.
        /// </summary>
        /// <param name="scalar">The scalar value.</param>
        public void DivideWithScalar(int scalar)
        {
            this.numeratorPolynomial.DivideWithScalar(scalar);
            this.denominatorPolynomial.DivideWithScalar(scalar);
        }

        /// <summary>
        /// Divides the polynomial through scalar.
        /// </summary>
        /// <param name="scalar">The scalar value.</param>
        public void DivideWithScalar(double scalar)
        {
            this.numeratorPolynomial.DivideWithScalar(scalar);
            this.denominatorPolynomial.DivideWithScalar(scalar);
        }

        /// <summary>
        /// Inverts the fraction of the rational polynomial.
        /// </summary>
        public void InvertFraction()
        {
            SimplePolynomial tempuri = this.numeratorPolynomial;

            this.numeratorPolynomial = this.denominatorPolynomial;
            this.denominatorPolynomial = tempuri;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first polynomial.</param>
        /// <param name="b">The second polynomial.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(SimpleRationalPolynomial a, SimpleRationalPolynomial b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return ((a.NumeratorPolynomial == b.NumeratorPolynomial)
                    && (a.DenominatorPolynomial == b.DenominatorPolynomial));
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first polynomial.</param>
        /// <param name="b">The second polynomial.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(SimpleRationalPolynomial a, SimpleRationalPolynomial b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first polynomial.</param>
        /// <param name="b">The second polynomial.</param>
        /// <returns>The result of the operator.</returns>
        public static SimpleRationalPolynomial operator +(SimpleRationalPolynomial a, SimpleRationalPolynomial b)
        {
            if (a == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("b");
            }

            return new SimpleRationalPolynomial((a.NumeratorPolynomial * b.DenominatorPolynomial)
                                                + (b.NumeratorPolynomial * a.DenominatorPolynomial),
                                                a.DenominatorPolynomial
                                                * b.DenominatorPolynomial);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first polynomial.</param>
        /// <param name="b">The second polynomial.</param>
        /// <returns>The result of the operator.</returns>
        public static SimpleRationalPolynomial operator -(SimpleRationalPolynomial a, SimpleRationalPolynomial b)
        {
            if (a == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("b");
            }

            return new SimpleRationalPolynomial((a.NumeratorPolynomial * b.DenominatorPolynomial)
                                                - (b.NumeratorPolynomial * a.DenominatorPolynomial),
                                                a.DenominatorPolynomial
                                                * b.DenominatorPolynomial);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The first polynomial.</param>
        /// <param name="b">The second polynomial.</param>
        /// <returns>The result of the operator.</returns>
        public static SimpleRationalPolynomial operator *(SimpleRationalPolynomial a, SimpleRationalPolynomial b)
        {
            if (a == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("b");
            }

            return new SimpleRationalPolynomial(a.NumeratorPolynomial * b.NumeratorPolynomial,
                                                a.DenominatorPolynomial * b.DenominatorPolynomial);
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="a">The first polynomial.</param>
        /// <param name="b">The second polynomial.</param>
        /// <returns>The result of the operator.</returns>
        public static SimpleRationalPolynomial operator /(SimpleRationalPolynomial a, SimpleRationalPolynomial b)
        {
            if (a == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (SimpleRationalPolynomial) null)
            {
                throw new ArgumentNullException("b");
            }

            SimpleRationalPolynomial tempuri = b.Copy();

            tempuri.InvertFraction();

            return a * tempuri;
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return "(" + this.numeratorPolynomial.ToString() + ") / ("
                   + this.denominatorPolynomial.ToString() + ")";
        }
    }
}